﻿using Padsurface.CoreAudio.Wrappers.Model;
using Padsurface.CoreAudio.Wrappers.Services.Interfaces;
using System;
using System.Composition;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;

namespace Padsurface.Audio.Wave
{
    [Export]
    public class WaveFileInput : IDisposable
    {
        private readonly AudioStream _audioStream;
        private readonly IAudioClientActivator _audioClientActivator;
        private readonly IAudioCaptureClientActivator _audioCaptureClientActivator;

        public bool IsRecording { get; private set; }
        public StorageFile StorageFile { get; private set; }
        public TimeSpan Length 
        {
            get
            {
                var result = (_totalBytesRecorded / (_audioStream.MixFormat.SamplesPerSec * _audioStream.MixFormat.Channels * _audioStream.MixFormat.BitsPerSample / 8));

                return TimeSpan.FromSeconds(result);
            }
        }

        private Stream _fileStream;
        private BinaryWriter _binaryWriter;
        private int _dataSizePosition;
        private int _totalBytesRecorded;

        [ImportingConstructor]
        public WaveFileInput(IAudioClientActivator audioClientActivator, IAudioCaptureClientActivator audioCaptureClientActivator)
        {
            if (audioClientActivator == null) throw new ArgumentNullException("audioClientActivator");
            if (audioCaptureClientActivator == null) throw new ArgumentNullException("audioCaptureClientActivator");

            _audioClientActivator = audioClientActivator;
            _audioCaptureClientActivator = audioCaptureClientActivator;

            _audioStream = new AudioStream(_audioClientActivator, audioCaptureClientActivator);
            _audioStream.AudioStreamBufferCaptured += OnAudioStreamBufferCaptured;
        }

        public async Task StartRecording(StorageFile storageFile)
        {
            if (storageFile == null) throw new ArgumentNullException("storageFile");

            StorageFile = storageFile;
            _fileStream = StorageFile.OpenStreamForWriteAsync().Result;
            _binaryWriter = new BinaryWriter(_fileStream);
            _totalBytesRecorded = 0;

            await _audioStream.Initialize();

            _binaryWriter.WriteHeader(_audioStream.MixFormat, out _dataSizePosition);

            _audioStream.Start();
            IsRecording = true;
        }

        public async Task StopRecording()
        {
            await _audioStream.Stop();

            _binaryWriter.UpdateHeader(_dataSizePosition, _totalBytesRecorded);
            _binaryWriter.Flush();

            Dispose();
            IsRecording = false;
        }

        private void OnAudioStreamBufferCaptured(object sender, AudioStreamBufferCapturedEventArgs e)
        {
            _fileStream.Write(e.Buffer, 0, e.Size);

            _totalBytesRecorded += e.Size;
        }

        public void Dispose()
        {
            if(_binaryWriter != null)
                _binaryWriter.Dispose();

            if(_fileStream != null)
                _fileStream.Dispose();

            if (_audioStream != null)
                _audioStream.Dispose();
        }
    }
}
